/**
 * 
 */
package de.cbf.cam.dsa.tavern.carte.controller;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.IndexedPropertyChangeEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import de.cbf.cam.dsa.tavern.carte.exceptions.WrongTypeProvidedException;
import de.cbf.cam.dsa.tavern.carte.model.entities.Addendum;
import de.cbf.cam.dsa.tavern.carte.model.entities.Ingredient;
import de.cbf.cam.dsa.tavern.carte.model.entities.Processing;
import de.cbf.cam.dsa.tavern.carte.model.entities.interfaces.IEditableEntity;
import de.cbf.cam.dsa.tavern.carte.ui.CompositeEditIngredient;
import de.cbf.cam.dsa.tavern.carte.ui.interfaces.ICompositeEditEntity;

/**
 * @author Stefan Alter
 *
 */
public class ControllerEditIngredient extends ControllerEditEntity {
	private Iterator<Processing> iteratorAllProcessings;
	/**
	 * 
	 */
	public ControllerEditIngredient() {		
	}

	/* (non-Javadoc)
	 * @see de.cbf.cam.dsa.tavern.carte.controller.ControllerEditEntity#checkEntity(de.cbf.cam.dsa.tavern.carte.model.entities.interfaces.IEditableEntity)
	 */
	@Override
	protected boolean checkEntity(IEditableEntity value)
			throws IllegalArgumentException {
		if (value instanceof Ingredient) {
			return true;
		}
		if(value == null){
			throw new WrongTypeProvidedException(Ingredient.class.getName(),"null");	
		}
		throw new WrongTypeProvidedException(Ingredient.class, value.getClass());	
	}

	/* (non-Javadoc)
	 * @see de.cbf.cam.dsa.tavern.carte.controller.ControllerEditEntity#checkView(de.cbf.cam.dsa.tavern.carte.ui.interfaces.ICompositeEditEntity)
	 */
	@Override
	protected boolean checkView(ICompositeEditEntity value)
			throws IllegalArgumentException {
		if (value instanceof CompositeEditIngredient) {			
			return true;
		}
		if(value == null){
			throw new WrongTypeProvidedException(CompositeEditIngredient.class.getName(),"null");	
		}
		throw new WrongTypeProvidedException(CompositeEditIngredient.class, value.getClass());	
	}

	/* (non-Javadoc)
	 * @see de.cbf.cam.dsa.tavern.carte.controller.ControllerEditEntity#registerViewListenersAndInit(de.cbf.cam.dsa.tavern.carte.ui.interfaces.ICompositeEditEntity)
	 */
	@Override
	protected boolean registerViewListenersAndInit(ICompositeEditEntity value) {
		if (value instanceof CompositeEditIngredient) {
			CompositeEditIngredient composite = (CompositeEditIngredient) value;
			//register view listeners			
			composite.registerNameModifiedListener(modifyName);
			composite.registerNowIngredientListener(nowIngredient);
			composite.registerNowCategoryListener(nowCategory);
			composite.registerParentChangedListener(parentChanged);
			composite.registerDeleteParentListener(parentRemoved);
			composite.registerCategoriesChangedListener(categoryAdded);
			composite.registerDeleteLastCategoryListener(categoryRemoved);
			composite.registerDescriptionModifiedListener(modifyDescription);
			composite.registerPriceModifiedListener(modifyPrice);							
			composite.registerCountsModifiedListener(countsModified);
			composite.registerAllProcessingSelectedListener(allProcessingSelected);
			composite.registerPossibleProcessingSelectedListener(possibleProcessingSelected);
			composite.registerAddendumSelectedListener(addendumSelected);
			composite.registerAddAddendumListener(addAddendum);
			composite.registerRemoveAddendumListener(removeAddendum);
			composite.registerAddProcessingListener(addPossibleProcessing);
			composite.registerRemoveProcessingListener(removePossibleProcessing);		
			//init view
			if(iteratorAllProcessings != null){
				//if we have a not yet applied iterator for root ingredients, we apply it
				composite.setAllPossibleProcessings(iteratorAllProcessings);
			}
			return true;
		}else{
			return false;
		}		
	}

	/* (non-Javadoc)
	 * @see de.cbf.cam.dsa.tavern.carte.controller.ControllerEditEntity#deRegisterViewListeners(de.cbf.cam.dsa.tavern.carte.ui.interfaces.ICompositeEditEntity)
	 */
	@Override
	protected boolean deRegisterViewListeners(ICompositeEditEntity value) {
		if (value instanceof CompositeEditIngredient) {
			CompositeEditIngredient composite = (CompositeEditIngredient) value;
			composite.deRegisterNameModifiedListener(modifyName);
			composite.deRegisterNowIngredientListener(nowIngredient);
			composite.deRegisterNowCategoryListener(nowCategory);
			composite.deRegisterParentChangedListener(parentChanged);
			composite.deRegisterDeleteParentListener(parentRemoved);
			composite.deRegisterCategoriesChangedListener(categoryAdded);
			composite.deRegisterDeleteLastCategoryListener(categoryRemoved);
			composite.deRegisterDescriptionModifiedListener(modifyDescription);
			composite.deRegisterPriceModifiedListener(modifyPrice);				
			composite.deRegisterCountsModifiedListener(countsModified);
			composite.deRegisterAllProcessingSelectedListener(allProcessingSelected);
			composite.deRegisterPossibleProcessingSelectedListener(possibleProcessingSelected);
			composite.deRegisterAddendumSelectedListener(addendumSelected);
			composite.deRegisterAddAddendumListener(addAddendum);
			composite.deRegisterRemoveAddendumListener(removeAddendum);
			composite.deRegisterAddProcessingListener(addPossibleProcessing);
			composite.deRegisterRemoveProcessingListener(removePossibleProcessing);
			return true;
		}else{
			return false;
		}		
	}

	/* (non-Javadoc)
	 * @see de.cbf.cam.dsa.tavern.carte.controller.ControllerEditEntity#getPCLforEntity()
	 */
	@Override
	protected PropertyChangeListener getPCLforEntity() {
		return pclIngredient;
	}
	private final PropertyChangeListener pclIngredient = new PCLIngredient();
	private class PCLIngredient implements PropertyChangeListener{
		@Override
		public void propertyChange(PropertyChangeEvent evt) {
			if (view instanceof CompositeEditIngredient) {
				CompositeEditIngredient composite = (CompositeEditIngredient) view;
				if (evt instanceof IndexedPropertyChangeEvent) {					
//					IndexedPropertyChangeEvent idxEvent = (IndexedPropertyChangeEvent) evt;	
					//TODO fineGrain property update
					composite.setEntity(entity);
				}else{
					//brutal update, because we do not know which property changed
					composite.setEntity(entity);
				}
			}
				
		}		
	}
	
	public void setAllProcessings(Iterator<Processing> value){
		this.iteratorAllProcessings = value;
	}
	
//	===================================
//		business logic
//	===================================
	/**
	 * called from outside to tell that controller should assume a selected ingredient<br>
	 * mainly for enabling of buttons
	 * @param value : the ingredient selected
	 */
	public void ingredientSelected(Ingredient value){
		if (view instanceof CompositeEditIngredient) {
			CompositeEditIngredient composite = (CompositeEditIngredient) view;	
			if(entity != null){
				composite.setButtonToParentEnabled(true);
				composite.setButtonToCategoriesEnabled(true);			
				composite.setButtonAddAddendumEnabled(checkAddendumForPossibleProcessing(value));				
			}			
		}
	}	
	
	/**
	 * method to check if provided Ingredient can be used with selected processing
	 * @param value
	 * @return
	 */
	private boolean checkAddendumForPossibleProcessing(Ingredient value){
		//port check
		if(value == null) return false; //no ingredient can never be possible ingredient
		//cast to our view
		if (view instanceof CompositeEditIngredient) {
			CompositeEditIngredient composite = (CompositeEditIngredient) view;
			//get selected processing
			Processing processing = composite.getSelectedPossibleProcessing();
			if(processing != null){//if <null> no processing is selected
				//there is a processing selected
				Iterator<String> iterator = processing.iteratorPossibleAddendumCategories();
				//ask all possible categories if there is one with ingredient name
				while (iterator.hasNext()) {
					String name = (String) iterator.next();
					if(value.getName().equals(name)){
						//ingredient name is in possible categories -> is possible
						return true;
					}
				}
			}			
		}
		return false;
	}
	
	public void doNameModify() {
		if (view instanceof CompositeEditIngredient) {
			CompositeEditIngredient composite = (CompositeEditIngredient) view;
			String name = composite.getIngredientName();
			if (entity instanceof Ingredient) {
				Ingredient ingredient = (Ingredient) entity;
				ingredient.setName(name);
				testUnsavedChanges();
			}			
		}		
	}
	
	public void doRemoveAddendum() {
		if (view instanceof CompositeEditIngredient) {
			CompositeEditIngredient composite = (CompositeEditIngredient) view;
			Processing processing = composite.getSelectedPossibleProcessing();
			if(processing != null){
				Addendum addendum = composite.getSelectedAddendum();
				processing.removeAddendum(addendum);										
			}
			composite.setButtonRemoveAddendumEnabled(false);
		}
	}	

	public void doRemovePossibleProcessing() {
		if (view instanceof CompositeEditIngredient) {
			CompositeEditIngredient composite = (CompositeEditIngredient) view;
			Processing desiredProcessing = composite.getSelectedPossibleProcessing();
			if(desiredProcessing != null){
				desiredProcessing.setAddendums(new ArrayList<Addendum>());//delete addendums before deleting processing
				if (entity instanceof Ingredient) {
					Ingredient ingredient = (Ingredient) entity;
					ingredient.removePossibleProcessing(desiredProcessing);			
					composite.setButtonRemoveProcessingEnabled(false);	
				}
			}			
		}		
	}

	public void doAddPossibleProcessing() {
		if (view instanceof CompositeEditIngredient) {
			CompositeEditIngredient composite = (CompositeEditIngredient) view;
			Processing desiredProcessing = composite.getSelectedAllProcessing();
			if(desiredProcessing != null){
				if (entity instanceof Ingredient) {
					Ingredient ingredient = (Ingredient) entity;
					ingredient.addPossibleProcessing(desiredProcessing);
					composite.setButtonAddProcessingEnabled(false);
					composite.setSelectedPossibleProcessingIndex(desiredProcessing.getName());
				}				
			}		
		}		
	}

	public void doAddendumSelect() {
		if (view instanceof CompositeEditIngredient) {
			CompositeEditIngredient composite = (CompositeEditIngredient) view;
			composite.setButtonRemoveAddendumEnabled(true);
		}	
	}

	public void doPossibleProcessingSelect() {
		if (view instanceof CompositeEditIngredient) {
			CompositeEditIngredient composite = (CompositeEditIngredient) view;
			Processing processing = composite.getSelectedPossibleProcessing();
			if(processing != null){				
				composite.setPossibleAddendums(processing.iteratorAddendums());
			}			
			composite.setButtonRemoveProcessingEnabled(true);
		}
	}

	public void doAllProcessingSelect() {
		if (view instanceof CompositeEditIngredient) {
			CompositeEditIngredient composite = (CompositeEditIngredient) view;	
			Processing processingSelected = composite.getSelectedAllProcessing();
			if (entity instanceof Ingredient) {
				Ingredient ingredient = (Ingredient) entity;
				Iterator<Processing> iterator = ingredient.iteratorPossibleProcessings();
				boolean buttonEnabled = true;
				while (iterator.hasNext()) {
					Processing processing = (Processing) iterator.next();
					if(processing.getName().equals(processingSelected.getName())){
						buttonEnabled = false;
					}
				}
				composite.setButtonAddProcessingEnabled(buttonEnabled);
			}			
		}
	}

	public void doCountsModify() {
		if (view instanceof CompositeEditIngredient) {
			CompositeEditIngredient composite = (CompositeEditIngredient) view;	
			Collection<String> counts = composite.getCounts();
			if (entity instanceof Ingredient) {
				Ingredient ingredient = (Ingredient) entity;
				ingredient.setCountTypes(counts);
			}
		}		
	}

	public void doCategoryRemove() {
		if (entity instanceof Ingredient) {
			Ingredient ingredient = (Ingredient) entity;	
			Iterator<String> iterator = ingredient.iteratorCategoryTypes();
			String category = "";
			while (iterator.hasNext()) {
				 category = (String) iterator.next();				
			}
			ingredient.removeCategoryType(category);
			testUnsavedChanges();
		}
	}

	


	public void doParentRemove() {
		if (entity instanceof Ingredient) {
			Ingredient ingredient = (Ingredient) entity;
			Ingredient parent = ingredient.getParent();
			if(parent != null){
				parent.removeSubIngredient(ingredient);
			}			
			testUnsavedChanges();
		}
	}

	
	public void doNowCategory() {
		if (view instanceof CompositeEditIngredient) {
			CompositeEditIngredient composite = (CompositeEditIngredient) view;
			boolean isCategory = composite.getIsCategory();
			if (entity instanceof Ingredient) {
				Ingredient ingredient = (Ingredient) entity;	
				int type = ingredient.getType();
				//is category and should become -> do nothing
				if(isCategory && ((type&Ingredient.TYPE_Category) != 0)){ 
					return;
				}
				//is NOT category and should NOT become -> do nothing
				if( ! isCategory && ((type&Ingredient.TYPE_Category) == 0)){ 
					return;
				}	
				//is NOT category and should become -> add
				if(isCategory && ((type&Ingredient.TYPE_Category) == 0)){
					type = type + Ingredient.TYPE_Category;			
				}else{
					//is category and should NOT become -> remove
					type = type - Ingredient.TYPE_Category;
				}
				ingredient.setType(type);
				testUnsavedChanges();
			}
		}	
	}

	public void doNowIngredient() {
		if (view instanceof CompositeEditIngredient) {
			CompositeEditIngredient composite = (CompositeEditIngredient) view;
			
			boolean isIngredient = composite.getIsIngredient();
			if (entity instanceof Ingredient) {
				Ingredient ingredient = (Ingredient) entity;	
				
				int type = ingredient.getType();
				//is ingredient and should become -> do nothing
				if(isIngredient && ((type&Ingredient.TYPE_Ingredient) != 0)){ 
					return;
				}
				//is NOT  ingredient and should NOT become -> do nothing
				if( ! isIngredient && ((type&Ingredient.TYPE_Ingredient) == 0)){ 
					return;
				}	
				//is NOT ingredient and should become -> add
				if(isIngredient && ((type&Ingredient.TYPE_Ingredient) == 0)){
					type = type + Ingredient.TYPE_Ingredient;	
					composite.setIngredientSetEnabled(true);
				}else{
					//is ingredient and should NOT become -> remove
					type = type - Ingredient.TYPE_Ingredient;
					composite.setButtonAddProcessingEnabled(false);
					composite.setIngredientSetEnabled(false);
					ingredient.setPossibleProcessings(null);//clear possibleProcessings
				}
				ingredient.setType(type);	
				//
				testUnsavedChanges();
			}
		}
	}
		

	public void doPriceModify() {
		if (view instanceof CompositeEditIngredient) {
			CompositeEditIngredient composite = (CompositeEditIngredient) view;
			Double basePrice = composite.getPrice();
			if(basePrice == null)return;
			if (entity instanceof Ingredient) {
				Ingredient ingredient = (Ingredient) entity;	
				ingredient.setBasePrice(basePrice);
				testUnsavedChanges();
			}		
		}		
	}

	public void doDescriptionModify() {
		if (view instanceof CompositeEditIngredient && entity instanceof Ingredient) {
			CompositeEditIngredient composite = (CompositeEditIngredient) view;
			Ingredient ingredient = (Ingredient) entity;
			//
			String description = composite.getDescription();				
			ingredient.setDescription(description);
			testUnsavedChanges();			
		}
	}
	public void doChangeParentTo(Ingredient value) {
		if (entity instanceof Ingredient) {
			Ingredient ingredient = (Ingredient) entity;
			value.addSubIngredient(ingredient);
			testUnsavedChanges();	
		}		
	}
	public void doAddCategory(Ingredient value) {
		if (entity instanceof Ingredient) {
			Ingredient ingredient = (Ingredient) entity;
			ingredient.addCategoryType(value.getName());
			testUnsavedChanges();	
		}
	}
	public void doAddAddendum(Ingredient value) {
		if (view instanceof CompositeEditIngredient) {
			CompositeEditIngredient composite = (CompositeEditIngredient) view;			
			//
			Processing processing = composite.getSelectedPossibleProcessing();
			if(processing != null){
				processing.addAddendum(value);
				testUnsavedChanges();	
			}			
		}		
	}
//	--------------- signals ---------------------
	
	private Collection<ActionListener> changeParentListeners = new ArrayList<ActionListener>();
	public boolean registerChangeParentListener(ActionListener listener){
		return changeParentListeners.add(listener);
	}
	public boolean deRegisterChangeParentListener(ActionListener listener){
		return changeParentListeners.remove(listener);
	}	
	public void signalChangeParent() {
		ActionEvent event = new ActionEvent(ControllerEditIngredient.this, ActionEvent.ACTION_PERFORMED, "ChangeParent");
		for (ActionListener listener : changeParentListeners) {
			listener.actionPerformed(event);
		}
	}
	private Collection<ActionListener> addCategoryListeners = new ArrayList<ActionListener>();
	public boolean registerAddCategoryListener(ActionListener listener){
		return addCategoryListeners.add(listener);
	}
	public boolean deRegisterAddCategoryListener(ActionListener listener){
		return addCategoryListeners.remove(listener);
	}	
	public void signalAddCategory() {
		ActionEvent event = new ActionEvent(ControllerEditIngredient.this, ActionEvent.ACTION_PERFORMED, "AddCategory");
		for (ActionListener listener : addCategoryListeners) {
			listener.actionPerformed(event);
		}
	}
	private Collection<ActionListener> addAddendumListeners = new ArrayList<ActionListener>();
	public boolean registerAddAddendumListener(ActionListener listener){
		return addAddendumListeners.add(listener);
	}
	public boolean deRegisterAddAddendumListener(ActionListener listener){
		return addAddendumListeners.remove(listener);
	}	
	public void signalAddAddendum() {
		ActionEvent event = new ActionEvent(ControllerEditIngredient.this, ActionEvent.ACTION_PERFORMED, "AddAddendum");
		for (ActionListener listener : addAddendumListeners) {
			listener.actionPerformed(event);
		}
	}
		
	// composite ingredient based =============================
	private ModifiedName modifyName = new ModifiedName();
	private class ModifiedName implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {			
			doNameModify();
		}		
	}	
	private ModifiedDescription modifyDescription = new ModifiedDescription();
	private class ModifiedDescription implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {			
			doDescriptionModify();
		}		
	}	
	private ModifiedPrice modifyPrice = new ModifiedPrice();
	private class ModifiedPrice implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {			
			doPriceModify();
		}		
	}	
	
	private NowIngredient nowIngredient = new NowIngredient();
	private class NowIngredient implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {			
			doNowIngredient();
		}		
	}
	
	private NowCategory nowCategory = new NowCategory();
	private class NowCategory implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {			
			doNowCategory();
		}			
	}	
	
	private ParentChanged parentChanged = new ParentChanged();
	private class ParentChanged implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {			
			signalChangeParent();
		}		
	}
	
	private ParentRemoved parentRemoved = new ParentRemoved();
	private class ParentRemoved implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {			
			doParentRemove();
		}		
	}
	
	private CategoryAdded categoryAdded = new CategoryAdded();
	private class CategoryAdded implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {			
			signalAddCategory();
		}		
	}
	
	private CategoryRemoved categoryRemoved = new CategoryRemoved();
	private class CategoryRemoved implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {			
			doCategoryRemove();
		}		
	}
	//counts =======================================
	private CountsModifiedAdvancer countsModified = new CountsModifiedAdvancer();	
	private class CountsModifiedAdvancer implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {
			doCountsModify();
		}

		
	}
	//processing and addendum =======================================
	//selection
	private AllProcessingSelected allProcessingSelected = new AllProcessingSelected();
	private class AllProcessingSelected implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {
			doAllProcessingSelect();
		}		
	}
	private PossibleProcessingSelected possibleProcessingSelected = new PossibleProcessingSelected();
	private class PossibleProcessingSelected implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {	
			doPossibleProcessingSelect();
		}		
	}
	
	private AddendumSelected addendumSelected = new AddendumSelected();
	private class AddendumSelected implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {
			doAddendumSelect();
		}		
	}
	
	private AddPossibleProcessing addPossibleProcessing = new AddPossibleProcessing();
	private class AddPossibleProcessing implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {
			doAddPossibleProcessing();	
		}		
	}
	
	private RemovePossibleProcessing removePossibleProcessing = new RemovePossibleProcessing();
	private class RemovePossibleProcessing implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {			
			doRemovePossibleProcessing();		
		}		
	}
	
	private AddAddendum addAddendum = new AddAddendum();
	private class AddAddendum implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {	
			signalAddAddendum();
		}		
	}
	
	private RemoveAddendum removeAddendum = new RemoveAddendum();
	private class RemoveAddendum implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {	
			doRemoveAddendum();
		}		
	}

	

}
